<?php


namespace app\common\http;

use think\facade\Db;
use think\worker\Server;
use Workerman\Worker as Workers;

class Worker extends Server
{
    protected $socket = 'websocket://0.0.0.0:2345';

    private array $connect = [];

    /**
     * 监听后台推送
     * @param $worker
     * @throws \Exception
     */
    public function onWorkerStart($worker)
    {
        // 开启一个内部端口，方便内部系统推送数据，Text协议格式 文本+换行符
        $inner_text_worker = new Workers('text://0.0.0.0:2346');
        $inner_text_worker->onMessage = function($connection, $data) {
            $data = json_decode($data, true);
            $ids  = explode(',', $data['data']['id']);
            foreach($this->connect as $conn) {
                $data['data']['id'] = $conn->worker->name;
                if ($data['id'] == 0 || in_array($data['id'], $ids)) {
                    $conn->send(json_encode($data));
                }
            }
            // 返回推送结果
            $connection->send('success');
        };
        $inner_text_worker->listen();
    }

    /**
     * 当有客户端发来消息时
     * @param $connection
     * @param $data {'type': '', 'data': []}
     */
    public function onMessage($connection, $data)
    {
        $param = json_decode($data, true);
        $data  = $param['data'] ?? '';
        print_r($param);
        switch ($param['type']) {
            // 在线
            case 'online':
                $connection->worker->name = $data['id'];
                $this->connect[$data['id']] = $connection;

                // 给好友发送上线通知
                // ['type' => 'online', 'data' => ['id' => $data['id']]]
                $this->sendAll($data['id'], $param);
                break;

            // 离线
            case 'offline':
                // 给好友发送离线通知
                // ['type' => 'offline', 'data' => ['id' => $data['id']]]
                $this->sendAll($data['id'], $param);
                break;

            // 添加好友
            case 'addList':
                // 自己添加到对方列表中
                $msg = ['type' => 'addList', 'data' => [
                    'type'     => 'friend',
                    'id'       => $data['id'],
                    'groupid'  => $data['group'],
                    'username' => $data['username'],
                    'avatar'   => $data['avatar'],
                    'sign'     => $data['sign'],
                ]];
                $this->sendOne($data['id'], $param);
                break;

            // 删除好友
            case 'removeList':
                // 自己从对方列表中删除
                $msg = ['type' => 'removeFriend', 'data' => [
                    'type'     => 'friend',
                    'id'       => $data['id'],
                ]];
                $this->sendOne($data['id'], $param);
                break;

            case 'addGroup':
                // [['id','username','avatar','status']]
                break;

            case 'message':
                $msg = [
                    'data' => [
                        "username"  => $data['mine']['username'],//消息来源用户名
                        "avatar"    => $data['mine']['avatar'],//消息来源用户头像
                        "id"        => $data['mine']['id'], //消息的来源ID（如果是私聊，则是用户id，如果是群聊，则是群组id）
                        "type"      => $data['to']['type'],//聊天窗口来源类型，从发送消息传递的to里面获取
                        "content"   => $data['mine']['content'],//消息内容
                        //"cid" => '',//消息id，可不传。除非你要对消息进行一些操作（如撤回）
                        "mine"      => false,//是否我发送的消息，如果为true，则会显示在右方
                        "fromid"    => $data['mine']['id'],//消息的发送者id（比如群组中的某个消息发送者），可用于自动解决浏览器多窗口时的一些问题
                        "timestamp" => time()*1000,//服务端时间戳毫秒数。注意：如果你返回的是标准的 unix 时间戳，记得要 *1000
                    ],
                    "type" => "message"
                ];

                // 在线
                if ($data['to']['type'] == 'friend') {
                    // 私聊
                    if (isset($this->connect[$data['to']['id']])) {
                        // 在线
                        $this->sendOne($data['to']['id'], $msg);
                    } else {
                        // 离线
                        $msg['data'] = [
                            'system'  => true, //系统消息
                            'id'      => 1, //聊天窗口ID
                            'type'    => "friend", //聊天窗口类型
                            'content' => '对方已掉线'
                        ];
                        $connection->send(json_encode($msg));
                    }
                } else {
                    // 群聊
                    foreach($this->connect as $connection) {
                        $connection->send(json_encode($msg));
                    }
                }
                break;
            default :
                print_r($data);
        }
    }

    /**
     * 当有客户端连接断开时
     * @param $connection
     */
    public function onClose($connection)
    {
        $id = $connection->worker->name;
        // 离线
        $msg = ['type' => 'offline', 'data' => ['id' => $id]];
        $this->sendAll($id, $msg);
        unset($this->connect[$id]);
    }

    /**
     * 获取我的好友
     * @param string $uid
     * @return array
     */
    private function getList($uid)
    {
        return Db::name('user_friends')->where('user_id', $uid)->column('friend_user_id');
    }

    /**
     * 给一个好友发送消息
     * @param string $uid 用户id
     * @param array $msg 消息
     */
    private function sendOne($uid, $msg)
    {
        $this->connect[$uid]->send(json_encode($msg));
    }

    /**
     * 给全部好友发送
     * @param string $uid 用户id
     * @param array $msg 消息
     */
    private function sendAll($uid, $msg)
    {
        $ids = $this->getList($uid);
        foreach($this->connect as $conn) {
            if (in_array($uid, $ids)) {
                $conn->send(json_encode($msg));
            }
        }
    }
}